How To Write Type-Safe CSS Modules

TypeScript

March 22, 2024

One of the benefits of using TypeScript is that it significantly reduces the occurrence of specific bugs, like typos; it even makes it easier to access prototype methods and perform refactoring. Bugs caught at compile time make for more uptime, happier customers, and less on-call stress for developers.

With TypeScript, it's easy to type our application's business logic and control flows, but what if we could make our CSS classes safe too? Having the correct CSS class names in place ensures that the intended styles are applied to a given component, preventing the styles from being misplaced due to typography errors.

In this article, we'll discuss what CSS Modules are, explore their developer experience shortcomings, and learn how to address them by using automation with TypeScript. Let's get started!

What are CSS Modules?

CSS Modules provide an approach to writing modular and scoped CSS styles in modern web apps. These styles are specific to your application's particular component or module. You can write CSS Modules by using regular CSS.

At build time, with Vite or other similar tools, the CSS Modules generate unique class names for each class defined in the CSS files. The generated class names are then used in JavaScript to refer to the CSS, thereby making the CSS modular and reusable without class name conflicts or unnecessary duplications.

Adding CSS Modules to your project

If you want to use CSS Modules in your next TypeScript app, you have several options. Modern build tools like Vite and Snowpack support CSS Modules out of the box, but you may need to include some minor configurations if you're using webpack. Once the build setup is done, you can add CSS files with the module.css extension following the CSS Modules convention:

Developer experience improvements

CSS Modules are a great tool, but since class names are generated at runtime and change between builds, it's hard to use them in a type-safe way. You could manually create types for each CSS Module using TypeScript definition files, but updating them is tedious. Let's suppose that a class name is added or removed from the CSS Module. In that case, the types must be manually updated, otherwise, the type safety won't work as expected.

Automatic typings

In this case, the automation solution is straightforward. We'll generate the types automatically instead of manually, and we'll provide a script to verify that the generated types are up-to-date to avoid incorrect CSS Module typings leaking into the compilation step.

Conclusion:

Working within the TypeScript ecosystem has great potential, but, when leaning too much on manual processes, it's easy to blow trust in the type-system or generate unnecessary friction. CSS Modules are great, and with a little bit of extra configuration, its easy to add type safety to the generated classes. You should automate the boring stuff so that your team can focus on building a great products instead. I hope you enjoyed this article, and be sure to leave a comment below if you have questions. Happy coding!